package com.acme.cli;

import com.acme.domain.account.Account;
import com.acme.domain.account.CheckingAccount;
import com.acme.domain.account.SavingAccount;
import com.acme.domain.bank.Bank;
import com.acme.domain.bank.Client;
import com.acme.domain.bank.Gender;
import com.acme.exceptions.BankException;
import com.acme.exceptions.BrokenBankFeedFileException;
import com.acme.exceptions.NoEnoughFundsException;
import com.acme.service.bank.BankDataLoader;
import com.acme.service.bank.BankService;
import com.acme.service.bank.impl.BankServiceImpl;

import java.io.IOException;


/**
 * The following scenarios are available:<br>
 * 1) "BankApplication -m" In-memory mode. Creates a few entities
 * programmatically, prints them.<br>
 * 2) "BankApplication -s" Forces to save bank to bank.ser file (file name can
 * not be specified for simplicity)<br>
 * 3) "BankApplication -l" Loads the bank from bank.ser file<br>
 * 4) "BankApplication -lf" Loads feed file from bank.data file, tries to add
 * data to the bank and then saves it to bank.ser file<br>
 * 5) "BankApplication -loadfeed <<file>>" Loads feed from <<file>>
 * 5) "BankApplication -loadbank <<file>>" Loads bank data from <<file>>
 */
public class BankApplication {
    private static final String USE_S_ARG_ONLY_ONE_TIME = "Use -s arg only one time";
    private static final String PLEASE_PASS_FILENAME_ALONG_WITH_LOADFEED_ARG = "Please pass filename along with -loadfeed arg";
    private static final String PLEASE_PASS_FILENAME_ALONG_WITH_LOADBANK_ARG = "Please pass filename along with -loadbank arg";
    private static final String ALREADY_LOADED = "Bank has already been loaded via another program argument. Only one option from the list {-m, -lf, -l, -loadfeed, -loadbank} may be specified at a time.";

    private static final String DEFAULT_BANK_SER_FILE_PATH = "bank.ser";
    private static final String DEFAULT_BANK_DATA_FILE_PATH = "bank.data";
    private static BankDataLoader bankDataLoader;
    private static BankService bankService;

    public static void main(final String[] args) throws Exception {
        initServices();

        Bank bank = null;
        boolean saveBank = false;
        boolean bankLoaded = false;
        for (int i = 0; i < args.length; i++) {
            String arg = args[i];

            if (arg.equals("-s")) {
                if (!saveBank) {
                    saveBank = true;
                } else {
                    System.out.println(USE_S_ARG_ONLY_ONE_TIME);
                    return;
                }
            } else {
                if (!bankLoaded) {
                    switch (arg) {
                        case "-m":
                            bank = createInMemoryBank();
                            break;
                        case "-l":
                            bank = loadBankFromDataSource(DEFAULT_BANK_SER_FILE_PATH);
                            break;
                        case "-lf":
                            bank = loadBankFromFeed(DEFAULT_BANK_DATA_FILE_PATH);
                            break;
                        case "-loadfeed":
                            if (i + 1 < args.length) {
                                bank = loadBankFromFeed(args[++i]);
                            } else {
                                System.out.println(PLEASE_PASS_FILENAME_ALONG_WITH_LOADFEED_ARG);
                                return;
                            }
                            break;
                        case "-loadbank":
                            if (i + 1 < args.length) {
                                bank = loadBankFromDataSource(args[++i]);
                            } else {
                                System.out.println(PLEASE_PASS_FILENAME_ALONG_WITH_LOADBANK_ARG);
                                return;
                            }
                    }
                    bankLoaded = true;
                } else {
                    System.out.println(ALREADY_LOADED);
                    return;
                }
            }
        }

        if (bank == null) {
            System.out.println("Bank is not loaded");
            return;
        }

        System.out.println(bank);

        if (saveBank) {
            saveTheBankToDataSource(bank);
        }
    }

    private static void initServices() {
        bankService = new BankServiceImpl();
        bankDataLoader = new BankDataLoader();
    }

    private static void saveTheBankToDataSource(final Bank bank) throws IOException {
        bankService.saveBank(bank, DEFAULT_BANK_SER_FILE_PATH);
    }

    private static Bank loadBankFromDataSource(String bankDataFilePath) throws IOException, ClassNotFoundException {
        return bankService.loadBank(bankDataFilePath);
    }

    private static Bank loadBankFromFeed(String feedFilePath) throws IOException, BrokenBankFeedFileException {
        Bank bank = new Bank();
        bankDataLoader.load(bank, feedFilePath);
        saveTheBankToDataSource(bank);
        return bank;
    }

    private static Bank createInMemoryBank() throws BankException {
        Bank bank = new Bank();
        Client client1 = bankService.addClient(bank, "John", Gender.MALE);
        Account account1 = new SavingAccount(100);
        Account account2 = new CheckingAccount(100, 0);
        client1.addAccount(account1);
        client1.addAccount(account2);

        Client client2 = bankService.addClient(bank, "Miranda", Gender.FEMALE);
        Account account3 = new SavingAccount(50);
        Account account4 = new CheckingAccount(75, 20);
        client2.addAccount(account3);
        client2.addAccount(account4);

        account1.deposit(100);

        account4.withdraw(90);

        try {
            account4.withdraw(10);
        } catch (NoEnoughFundsException e) {
            e.printStackTrace();
        }

        return bank;
    }
}